"""
追踪服务
"""
from typing import List, Optional, Tuple
from datetime import datetime, timedelta
from tortoise.exceptions import DoesNotExist, IntegrityError
from fastapi import HTTPException, status
from loguru import logger

from app.models.tracking import TrackingDevice, LocationHistory, GeofenceArea, GeofenceEvent
from app.models.cargo import Cargo
from app.models.equipment import Equipment
from app.schemas.tracking import (
    TrackingDeviceCreate, TrackingDeviceUpdate, TrackingDeviceResponse,
    LocationHistoryCreate, LocationHistoryResponse,
    GeofenceAreaCreate, GeofenceAreaUpdate, GeofenceAreaResponse,
    GeofenceEventCreate, GeofenceEventResponse,
    RealTimeLocationResponse
)
from app.services.cache_service import CacheService, PubSubService


class TrackingDeviceService:
    """追踪设备服务类"""
    
    @staticmethod
    async def create_device(device_data: TrackingDeviceCreate) -> TrackingDeviceResponse:
        """创建追踪设备"""
        try:
            # 检查设备ID是否重复
            existing_device = await TrackingDevice.get_or_none(device_id=device_data.device_id)
            if existing_device:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail="设备ID已存在"
                )
            
            # 检查关联的货物或装备是否存在
            if device_data.cargo_id:
                cargo = await Cargo.get_or_none(id=device_data.cargo_id)
                if not cargo:
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail="关联的货物不存在"
                    )
            
            if device_data.equipment_id:
                equipment = await Equipment.get_or_none(id=device_data.equipment_id)
                if not equipment:
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail="关联的装备不存在"
                    )
            
            # 创建追踪设备
            device = await TrackingDevice.create(**device_data.dict())
            
            logger.info(f"创建追踪设备成功: {device.device_id}")
            return TrackingDeviceResponse.from_orm(device)
            
        except IntegrityError as e:
            logger.error(f"创建追踪设备失败，数据完整性错误: {e}")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="数据完整性错误，请检查输入数据"
            )
        except Exception as e:
            logger.error(f"创建追踪设备失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="创建追踪设备失败"
            )
    
    @staticmethod
    async def get_device(device_id: int) -> TrackingDeviceResponse:
        """获取追踪设备详情"""
        try:
            device = await TrackingDevice.get(id=device_id)
            return TrackingDeviceResponse.from_orm(device)
        except DoesNotExist:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="追踪设备不存在"
            )
    
    @staticmethod
    async def update_device(device_id: int, device_data: TrackingDeviceUpdate) -> TrackingDeviceResponse:
        """更新追踪设备"""
        try:
            device = await TrackingDevice.get(id=device_id)
            
            # 检查关联的货物或装备是否存在
            if device_data.cargo_id:
                cargo = await Cargo.get_or_none(id=device_data.cargo_id)
                if not cargo:
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail="关联的货物不存在"
                    )
            
            if device_data.equipment_id:
                equipment = await Equipment.get_or_none(id=device_data.equipment_id)
                if not equipment:
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail="关联的装备不存在"
                    )
            
            # 更新设备信息
            update_data = device_data.dict(exclude_unset=True)
            await device.update_from_dict(update_data)
            await device.save()
            
            logger.info(f"更新追踪设备成功: {device.device_id}")
            return TrackingDeviceResponse.from_orm(device)
            
        except DoesNotExist:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="追踪设备不存在"
            )
        except Exception as e:
            logger.error(f"更新追踪设备失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="更新追踪设备失败"
            )
    
    @staticmethod
    async def delete_device(device_id: int) -> bool:
        """删除追踪设备"""
        try:
            device = await TrackingDevice.get(id=device_id)
            await device.delete()
            
            logger.info(f"删除追踪设备成功: {device.device_id}")
            return True
            
        except DoesNotExist:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="追踪设备不存在"
            )
        except Exception as e:
            logger.error(f"删除追踪设备失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="删除追踪设备失败"
            )
    
    @staticmethod
    async def list_devices(
        page: int = 1,
        size: int = 20,
        device_type: Optional[str] = None,
        status: Optional[str] = None,
        cargo_id: Optional[int] = None,
        equipment_id: Optional[int] = None
    ) -> Tuple[List[TrackingDeviceResponse], int]:
        """获取追踪设备列表"""
        try:
            # 构建查询
            query = TrackingDevice.all()
            
            # 按设备类型筛选
            if device_type:
                query = query.filter(device_type=device_type)
            
            # 按状态筛选
            if status:
                query = query.filter(status=status)
            
            # 按关联货物筛选
            if cargo_id:
                query = query.filter(cargo_id=cargo_id)
            
            # 按关联装备筛选
            if equipment_id:
                query = query.filter(equipment_id=equipment_id)
            
            # 获取总数
            total = await query.count()
            
            # 分页
            offset = (page - 1) * size
            devices = await query.offset(offset).limit(size)
            
            # 转换为响应模式
            device_responses = [TrackingDeviceResponse.from_orm(device) for device in devices]
            
            return device_responses, total
            
        except Exception as e:
            logger.error(f"获取追踪设备列表失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="获取追踪设备列表失败"
            )


class LocationService:
    """位置服务类"""
    
    @staticmethod
    async def record_location(location_data: LocationHistoryCreate) -> LocationHistoryResponse:
        """记录位置信息"""
        try:
            # 检查设备是否存在
            device = await TrackingDevice.get_or_none(id=location_data.device_id)
            if not device:
                raise HTTPException(
                    status_code=status.HTTP_400_BAD_REQUEST,
                    detail="追踪设备不存在"
                )
            
            # 创建位置记录
            location = await LocationHistory.create(**location_data.dict())

            # 缓存位置信息
            location_dict = location_data.dict()
            await CacheService.cache_device_location(device.device_id, location_dict)

            # 发布位置更新
            await PubSubService.publish_location_update(device.device_id, location_dict)

            # 清除实时位置列表缓存
            await CacheService.invalidate_device_cache(device.device_id)

            logger.info(f"记录位置信息成功: 设备{device.device_id}")
            return LocationHistoryResponse.from_orm(location)
            
        except Exception as e:
            logger.error(f"记录位置信息失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="记录位置信息失败"
            )
    
    @staticmethod
    async def get_device_locations(
        device_id: int,
        start_time: Optional[datetime] = None,
        end_time: Optional[datetime] = None,
        page: int = 1,
        size: int = 20
    ) -> Tuple[List[LocationHistoryResponse], int]:
        """获取设备位置历史"""
        try:
            # 检查设备是否存在
            device = await TrackingDevice.get_or_none(id=device_id)
            if not device:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail="追踪设备不存在"
                )
            
            # 构建查询
            query = LocationHistory.filter(device_id=device_id)
            
            # 时间范围筛选
            if start_time:
                query = query.filter(recorded_at__gte=start_time)
            if end_time:
                query = query.filter(recorded_at__lte=end_time)
            
            # 按时间倒序排列
            query = query.order_by("-recorded_at")
            
            # 获取总数
            total = await query.count()
            
            # 分页
            offset = (page - 1) * size
            locations = await query.offset(offset).limit(size)
            
            # 转换为响应模式
            location_responses = [LocationHistoryResponse.from_orm(location) for location in locations]
            
            return location_responses, total
            
        except Exception as e:
            logger.error(f"获取设备位置历史失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="获取设备位置历史失败"
            )
    
    @staticmethod
    async def get_latest_locations() -> List[RealTimeLocationResponse]:
        """获取所有设备的最新位置"""
        try:
            # 尝试从缓存获取
            cached_locations = await CacheService.get_realtime_locations()
            if cached_locations:
                return [RealTimeLocationResponse(**loc) for loc in cached_locations]

            # 获取所有活跃设备
            devices = await TrackingDevice.filter(status="active")
            
            real_time_locations = []
            
            for device in devices:
                # 获取设备最新位置
                latest_location = await LocationHistory.filter(
                    device_id=device.id
                ).order_by("-recorded_at").first()
                
                if latest_location:
                    # 获取关联信息
                    cargo_info = None
                    equipment_info = None
                    
                    if device.cargo_id:
                        cargo = await Cargo.get_or_none(id=device.cargo_id)
                        if cargo:
                            cargo_info = {
                                "id": cargo.id,
                                "name": cargo.name,
                                "code": cargo.code
                            }
                    
                    if device.equipment_id:
                        equipment = await Equipment.get_or_none(id=device.equipment_id)
                        if equipment:
                            equipment_info = {
                                "id": equipment.id,
                                "name": equipment.name,
                                "code": equipment.code
                            }
                    
                    real_time_location = RealTimeLocationResponse(
                        device_id=device.device_id,
                        device_type=device.device_type,
                        latitude=latest_location.latitude,
                        longitude=latest_location.longitude,
                        altitude=latest_location.altitude,
                        speed=latest_location.speed,
                        direction=latest_location.direction,
                        address=latest_location.address,
                        battery_level=device.battery_level,
                        signal_strength=device.signal_strength,
                        last_update=latest_location.recorded_at,
                        cargo_info=cargo_info,
                        equipment_info=equipment_info
                    )
                    
                    real_time_locations.append(real_time_location)

            # 缓存结果
            await CacheService.cache_realtime_locations(real_time_locations)

            return real_time_locations
            
        except Exception as e:
            logger.error(f"获取实时位置失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="获取实时位置失败"
            )


class GeofenceService:
    """地理围栏服务类"""

    @staticmethod
    async def create_geofence(geofence_data: GeofenceAreaCreate) -> GeofenceAreaResponse:
        """创建地理围栏"""
        try:
            # 验证围栏参数
            if geofence_data.fence_type == "circle":
                if not all([geofence_data.center_latitude, geofence_data.center_longitude, geofence_data.radius]):
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail="圆形围栏需要提供中心坐标和半径"
                    )
            elif geofence_data.fence_type == "polygon":
                if not geofence_data.polygon_points:
                    raise HTTPException(
                        status_code=status.HTTP_400_BAD_REQUEST,
                        detail="多边形围栏需要提供顶点坐标"
                    )

            # 创建地理围栏
            geofence = await GeofenceArea.create(**geofence_data.dict())

            logger.info(f"创建地理围栏成功: {geofence.name}")
            return GeofenceAreaResponse.from_orm(geofence)

        except Exception as e:
            logger.error(f"创建地理围栏失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="创建地理围栏失败"
            )

    @staticmethod
    async def update_geofence(geofence_id: int, geofence_data: GeofenceAreaUpdate) -> GeofenceAreaResponse:
        """更新地理围栏"""
        try:
            geofence = await GeofenceArea.get(id=geofence_id)

            # 更新围栏信息
            update_data = geofence_data.dict(exclude_unset=True)
            await geofence.update_from_dict(update_data)
            await geofence.save()

            logger.info(f"更新地理围栏成功: {geofence.name}")
            return GeofenceAreaResponse.from_orm(geofence)

        except DoesNotExist:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="地理围栏不存在"
            )
        except Exception as e:
            logger.error(f"更新地理围栏失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="更新地理围栏失败"
            )

    @staticmethod
    async def delete_geofence(geofence_id: int) -> bool:
        """删除地理围栏"""
        try:
            geofence = await GeofenceArea.get(id=geofence_id)
            await geofence.delete()

            logger.info(f"删除地理围栏成功: {geofence.name}")
            return True

        except DoesNotExist:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="地理围栏不存在"
            )
        except Exception as e:
            logger.error(f"删除地理围栏失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="删除地理围栏失败"
            )

    @staticmethod
    async def list_geofences(
        page: int = 1,
        size: int = 20,
        fence_type: Optional[str] = None,
        is_enabled: Optional[bool] = None
    ) -> Tuple[List[GeofenceAreaResponse], int]:
        """获取地理围栏列表"""
        try:
            # 构建查询
            query = GeofenceArea.all()

            # 按围栏类型筛选
            if fence_type:
                query = query.filter(fence_type=fence_type)

            # 按启用状态筛选
            if is_enabled is not None:
                query = query.filter(is_enabled=is_enabled)

            # 获取总数
            total = await query.count()

            # 分页
            offset = (page - 1) * size
            geofences = await query.offset(offset).limit(size)

            # 转换为响应模式
            geofence_responses = [GeofenceAreaResponse.from_orm(geofence) for geofence in geofences]

            return geofence_responses, total

        except Exception as e:
            logger.error(f"获取地理围栏列表失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="获取地理围栏列表失败"
            )

    @staticmethod
    async def check_geofence_violation(device_id: int, latitude: float, longitude: float) -> List[GeofenceEventResponse]:
        """检查地理围栏违规"""
        try:
            import math

            # 获取所有启用的地理围栏
            geofences = await GeofenceArea.filter(is_enabled=True)

            violations = []

            for geofence in geofences:
                is_inside = False

                if geofence.fence_type == "circle":
                    # 计算距离（使用Haversine公式）
                    lat1, lon1 = math.radians(float(latitude)), math.radians(float(longitude))
                    lat2, lon2 = math.radians(float(geofence.center_latitude)), math.radians(float(geofence.center_longitude))

                    dlat = lat2 - lat1
                    dlon = lon2 - lon1

                    a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
                    c = 2 * math.asin(math.sqrt(a))
                    distance = 6371000 * c  # 地球半径（米）

                    is_inside = distance <= float(geofence.radius)

                # 检查是否需要触发事件
                should_trigger = False
                event_type = None

                if geofence.rule_type == "enter" and is_inside:
                    should_trigger = True
                    event_type = "enter"
                elif geofence.rule_type == "exit" and not is_inside:
                    should_trigger = True
                    event_type = "exit"
                elif geofence.rule_type == "both":
                    should_trigger = True
                    event_type = "enter" if is_inside else "exit"

                if should_trigger:
                    # 检查是否已经有相同的事件（避免重复触发）
                    recent_event = await GeofenceEvent.filter(
                        device_id=device_id,
                        geofence_id=geofence.id,
                        event_type=event_type,
                        occurred_at__gte=datetime.now() - timedelta(minutes=5)
                    ).first()

                    if not recent_event:
                        # 创建地理围栏事件
                        event_data = GeofenceEventCreate(
                            device_id=device_id,
                            geofence_id=geofence.id,
                            event_type=event_type,
                            latitude=latitude,
                            longitude=longitude,
                            occurred_at=datetime.now()
                        )

                        event = await GeofenceEvent.create(**event_data.dict())
                        violations.append(GeofenceEventResponse.from_orm(event))

            return violations

        except Exception as e:
            logger.error(f"检查地理围栏违规失败: {e}")
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="检查地理围栏违规失败"
            )
